package simple.utils.tracking.storage;

import com.google.common.io.BaseEncoding;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import simple.utils.data.leveldb.AbstractLevelDB;
import simple.utils.data.leveldb.LevelDBFactory;
import simple.utils.data.serializers.ByteArraySerializer;
import simple.utils.data.serializers.GPBSerializer;
import simple.utils.data.serializers.Serializer;
import simple.utils.tracking.TrackingProto;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by martin on 5.5.14.
 */
public class LevelDBStorageFactory implements KeyValueStorageFactory, AutoCloseable {

    private static final Logger LOGGER = LoggerFactory.getLogger(LevelDBStorageFactory.class);
    private final Map<String, AbstractLevelDB> dbs = new ConcurrentHashMap<>();// one for every object type
    private final ReentrantLock lock = new ReentrantLock();
    private final String rootDir;

    public LevelDBStorageFactory(String rootDir) {
        this.rootDir = rootDir;
    }

    @Override
    public AbstractLevelDB<byte[], TrackingProto.TrackingRecord> get(String name) {
        // safe name for file system
        byte[] nameBytes = name.getBytes(StandardCharsets.UTF_8);
        name = BaseEncoding.base16().lowerCase().encode(nameBytes);
        lock.lock();
        try {
            AbstractLevelDB<byte[], TrackingProto.TrackingRecord> db = dbs.get(name);
            if (db == null) {
                File file = new File(rootDir + File.separator + name);
                try {
                    Serializer<byte[]> keySer = new ByteArraySerializer();
                    Serializer<TrackingProto.TrackingRecord> s = GPBSerializer.get(TrackingProto.TrackingRecord.getDefaultInstance());
                    db = LevelDBFactory.create(file, keySer, s);
                    dbs.put(name, db);
                    return db;
                } catch (IOException e) {
                    LOGGER.error("Error creating database at " + file.getAbsolutePath());
                    e.printStackTrace();
                    return null;
                }
            } else {
                return db;
            }
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void close() throws Exception {
        Iterator<AbstractLevelDB> it = dbs.values().iterator();
        while (it.hasNext()) {
            it.next().close();
        }
    }
}
